package restful.servlet;

import static java.text.MessageFormat.*;

import java.lang.reflect.*;

import javax.ws.rs.*;

import restful.*;

public class RestfulScanner {

	private final RestfulProtocol protocol;

	public RestfulScanner(RestfulProtocol protocol) {
		this.protocol = protocol;
	}

	private String join(Path rootPath, Path path) {
		if (rootPath == null) return path.value();
		if (path == null) return rootPath.value();
		return format("{0}/{1}", rootPath.value(), path.value()).replaceAll("/$", "");
	}

	public RestfulProtocol scanning(String classSuffix, Class... classes) {
		for (Class scanningClass : classes) {
			protocol.topic(topicName(classSuffix, scanningClass));
			tryMapping(scanningClass);
		}
		return protocol;
	}

	private String topicName(String classSuffix, Class scanningClass) {
		return scanningClass.getSimpleName().replaceAll(classSuffix + "$", "");
	}

	private void tryMapping(Class scanningClass) {
		Path path = (Path) scanningClass.getAnnotation(Path.class);
		tryMapping(scanningClass.getMethods(), path);
	}

	private void tryMapping(Method[] methods, Path rootPath) {
		for (Method method : methods) {
			Path subPath = method.getAnnotation(Path.class);
			if (notDefined(rootPath, subPath)) continue;
			String path = join(rootPath, subPath);
			tryAdd(POST.class, path, method);
			tryAdd(GET.class, path, method);
			tryAdd(PUT.class, path, method);
			tryAdd(DELETE.class, path, method);
		}
	}

	private boolean notDefined(Path rootPath, Path path) {
		return rootPath == null && path == null;
	}

	private void tryAdd(Class verb, String path, Method method) {
		if (isAnnotated(method, verb)) {
			add(verb, method, path);
		}
	}

	private boolean isAnnotated(Method method, Class findVerb) {
		return method.getAnnotation(findVerb) != null;
	}

	private void add(Class verb, Method method, String path) {
		protocol.goal(method.getName());
		if (DELETE.class.equals(verb)) protocol.DELETE(path, method.getName());
		if (PUT.class.equals(verb)) protocol.PUT(path, method.getName());
		if (GET.class.equals(verb)) protocol.GET(path, method.getName());
		if (POST.class.equals(verb)) protocol.POST(path, method.getName());
	}

}